﻿#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

//指针和数组笔试题解析

//一维数组
//int main()
//{
//	int a[] = { 1,2,3,4 };
//	printf("%d\n", sizeof(a));//4*4=16
//	printf("%d\n", sizeof(a + 0));//a+0 其实是数组第一个元素的地址，是地址就是4/8字节
//	printf("%d\n", sizeof(*a));//*a是数组首元素，计算的是数组首元素的大小，单位是字节，4
//	printf("%d\n", sizeof(a + 1));//a+1是第二个元素的地址，是地址大小就是4/8
//	printf("%d\n", sizeof(a[1]));//a[1]是第二个元素，计算的是第二个元素的大小，4
//	printf("%d\n", sizeof(&a));//&a是整个数组的地址，整个数组的地址也是地址，是地址的大小就是4/8
//	//&a ---> 类型：int (*)[4]
//
//	printf("%d\n", sizeof(*&a));//&a是数组的地址，*&a就是拿到了数组，*&a --> a，a就是数组名,sizeof(*&a)-->sizeof(a),计算的是整个数组的大小，16字节
//	printf("%d\n", sizeof(&a + 1));//&a是整个数组的地址，&a+1，跳过整个数组，指向数组后边的空间，是一个地址，大小是4/8字节
//	printf("%d\n", sizeof(&a[0]));//&a[0]是首元素的地址，计算的是数组首元素地址的大小，4/8
//	printf("%d\n", sizeof(&a[0] + 1));//&a[0]+1是第二个元素的地址，地址的大小就是4/8
//	
//	printf("%d\n", sizeof(*(&a + 1)));//sizeof(*(&a + 1))的结果等同于sizeof(a),因为这里的指针运算和解引用类型都与a本身的类型有关，
//	                                  //而sizeof只关心类型的大小，不会实际访问内存，因此结果就是a的类型的大小，16字节
//	
//	printf("%d\n", sizeof(sizeof(a + 1)));//sizeof(sizeof(a + 1))的结果是sizeof(size_t),size_t是sizeof的返回类型，是无符号的整型类型(unsigned int)，
//										  //size_t的大小由编译环境决定：32位-4字节，64位-8字节
//	return 0;
//}

//字符数组
#include <string.h>
//int main()
//{
//	char arr[] = { 'a','b','c','d','e','f' };
//	printf("%d\n", sizeof(arr));//arr单独放在sizeof内部，计算的是在整个数组的大小，6字节
//	printf("%d\n", sizeof(arr + 0));//arr+0是数组首元素的地址，4/8
//	printf("%d\n", sizeof(*arr));//*arr是数组首元素，计算的是首元素的大小，1字节
//	printf("%d\n", sizeof(arr[1]));//arr[1]是第二个元素，大小1字节
//	printf("%d\n", sizeof(&arr));//&arr取出数组的地址，数组的地址也是地址，是地址大小就是4/8
//	printf("%d\n", sizeof(&arr + 1));//&arr+1跳过跳过整个数组，指向数组后边的空间的地址，4/8
//	printf("%d\n", sizeof(&arr[0] + 1));//&arr[0]+1是数组第二个元素的地址，4/8
//
//	//size_t strlen(const char* str)
//	//arr当作地址理解类型是char*,当作数组理解类型是char [6]
//	printf("%d\n", strlen(arr));//随机值，没有\0作为结束标志
//	printf("%d\n", strlen(arr + 0));//随机值，是数组首元素的地址，从首元素的地址开始计算字符串的大小
//	printf("%d\n", strlen(*arr));//strlen('a')-->strlen(97),非法访问,error
//	printf("%d\n", strlen(arr[1]));//'b'->98,和上面的代码类似，是非法访问，error
//	printf("%d\n", strlen(&arr));//&arr虽然是数组的地址，但是也是从数组起始位置开始的，计算的还是随机值
//	//类型是char (*)[6],这里的类型与上面的不同，会报警告
//	printf("%d\n", strlen(&arr + 1));
//	//&arr是数组的地址，&arr+1是跳过整个数组的地址，求字符串长度也是随机值
//	printf("%d\n", strlen(&arr[0] + 1));//是第二个元素的地址，是'b'的地址，求字符串长度也是随机值
//
//	return 0;
//}

//sizeof（操作符） 只关注占用内存空间的大小，单位是字节，不关心内存中存放的是什么，不以\0为结束标志，如果有\0，也会对它进行计算
//strlen（库函数） 是求字符串长度的，统计的是\0之前出现的字符个数，一定要找到\0才算结束，所以可能存在越界访问

//int main()
//{
//	char arr[] = "abcdef";//数组有7个元素
//	//[a b c d e f \0]
//	printf("%d\n", sizeof(arr));//7 - 数组名单独放在sizeof内部，计算的是整个数组的大小，单位是字节
//	printf("%d\n", sizeof(arr + 0));//arr+0是首元素的地址，大小是4/8
//	printf("%d\n", sizeof(*arr));//*arr是数组首元素，大小是1字节
//	printf("%d\n", sizeof(arr[1]));//arr[1]是数组的第二个元素，大小是1字节
//	printf("%d\n", sizeof(&arr));//&arr是数组的地址，数组的地址也是地址，大小是4/8字节
//	printf("%d\n", sizeof(&arr + 1));//&arr+1是跳过整个数组的地址，是4/8
//	printf("%d\n", sizeof(&arr[0] + 1));//是第二个元素的地址，4/8
//
//	printf("%d\n", strlen(arr));//6，arr是数组首元素的地址，strlen从首元素的地址开始统计\0之前出现的字符个数，是6
//	printf("%d\n", strlen(arr + 0));//arr+0是首元素的地址，同第一个，结果是6
//	printf("%d\n", strlen(*arr));//*arr是'a',是97，传给strlen是一个非法的地址，造成非法访问，error
//	printf("%d\n", strlen(arr[1]));//arr[1]是'b'-98，非法访问，error
//	printf("%d\n", strlen(&arr));//6，&arr虽然是数组的地址，但是也是从数组起始位置开始向后统计\0之前的字符个数，是6
//	printf("%d\n", strlen(&arr + 1));//&arr+1是跳过数组后的地址，统计的字符串长度是随机值
//	printf("%d\n", strlen(&arr[0] + 1));//是b的地址，从第二个字符往后统计字符串长度，大小是5
//
//	return 0;
//}

//int main()
//{
//	char* p = "abcdef";
//	printf("%d\n", sizeof(p));//p是指针变量，大小就是4/8字节
//	printf("%d\n", sizeof(p + 1));//p+1是b的地址，是地址，就是4/8字节
//	printf("%d\n", sizeof(*p));//*p是'a'，计算的是字符的大小，1字节
//	printf("%d\n", sizeof(p[0]));//p[0] -> *(p+0) -> *p,同上一个，是1字节
//	printf("%d\n", sizeof(&p));//&p是二级指针，是指针大小就是4/8字节
//	printf("%d\n", sizeof(&p + 1));//&p+1是跳过p变量后的地址，4/8
//	printf("%d\n", sizeof(&p[0] + 1));//p[0]就是'a',&p[0]就是a的地址，+1，就是b的地址，4/8字节
//
//	printf("%d\n", strlen(p));//6，求字符串长度
//	printf("%d\n", strlen(p + 1));//p+1是b的地址，求字符串长度是5
//	printf("%d\n", strlen(*p));//error，*p就是'a'
//	printf("%d\n", strlen(p[0]));//error,同上一个
//	printf("%d\n", strlen(&p));//&p是二级指针,&p拿到的是p这个指针变量的起始地址，从这里开始求字符串长度完全是随机值
//	printf("%d\n", strlen(&p + 1));//&p+1是跳过p变量的地址，从这里开始求字符串长度也是随机值
//	printf("%d\n", strlen(&p[0] + 1));//是b的地址，从b的地址向后数字符串长度是5
//
//	return 0;
//}

//二维数组
//int main()
//{
//	int a[3][4] = { 0 };
//	printf("%d\n", sizeof(a));//48 = 3*4*4
//	printf("%d\n", sizeof(a[0][0]));//4
//	printf("%d\n", sizeof(a[0]));//a[0]是第一行的数组名，数组名单独放在sizeof内部，计算的就是数组（第一行）的大小，16个字节
//	printf("%d\n", sizeof(a[0] + 1));//a[0]作为第一行的数组名，没有单独放在sizeof内部，没有取地址，表示的就是数组首元素的地址，就是a[0][0]的地址
//								    //a[0]+1就是第一行第二个元素的地址，是地址就是4/8字节
//	printf("%d\n", sizeof(*(a[0] + 1)));//是第一行第二个元素，计算的是元素的大小，4字节
//	printf("%d\n", sizeof(a + 1));//a是二维数组的数组名，数组名表示首元素的地址，就是第一行的地址，a+1就是第二行的地址，4/8
//	//a - int (*)[4]
//	//a+1 - int (*)[4]
//	printf("%d\n", sizeof(*(a + 1)));//a+1表示第二行的地址，*(a+1)表示的就是第二行，*(a+1) -> a[1],16字节
//	printf("%d\n", sizeof(&a[0] + 1));//&a[0]是第一行的地址，&a[0]+1是第二行的地址，地址的大小是4/8
//	printf("%d\n", sizeof(*(&a[0] + 1)));//对第二行进行解引用，得到的就是第二行，计算的是第二行的大小，16
//	printf("%d\n", sizeof(*a));//a表示首元素的地址，就是第一行的地址，*a就是第一行，计算的是第一行的大小，16
//	//*a -> *(a+0) -> a[0]
//	printf("%d\n", sizeof(a[3]));//16字节 int [4]
//	//如果数组存在第四行，a[3]就是第四行的数组名，数组名单独放在sizeof内部，计算的是第四行的大小
//	//可以通过以下的例子来理解：
//	int a = 10;
//	printf("%d\n", sizeof(a));
//	printf("%d\n", sizeof(int));//sizeof只关心类型的大小，不会去实际访问内存，因此，我们知道10是int类型的，就可以通过int来确定大小
//
//	return 0;
//}

//int main()
//{
//	short s = 3;
//	int a = 10;
//	printf("%d\n", sizeof(s = a + 3));//2  还是那句话sizeof只关心类型的大小，不会去实际访问内存，即sizeof内部的表达式不会进行计算，
//										//s的类型是short，2个字节
//	printf("%d\n", s);//3
//
//	return 0;
//}

//C语言中，表达式有2个属性：
//2+3
//值属性：5
//类型属性：int

//数组名的意义：
//1. sizeof(数组名)，这⾥的数组名表⽰整个数组，计算的是整个数组的⼤⼩。
//2. & 数组名，这⾥的数组名表⽰整个数组，取出的是整个数组的地址。
//3. 除此之外所有的数组名都表⽰⾸元素的地址。

//指针笔试题
//1
//int main()
//{
//	int a[5] = { 1, 2, 3, 4, 5 };
//	int* ptr = (int*)(&a + 1);
//	printf("%d,%d", *(a + 1), *(ptr - 1));//2  5
//	return 0;
//}

//2
//假设结构体的⼤⼩是20个字节 
//程序输出的结果是啥？ 
//struct Test
//{
//	int Num;
//	char* pcName;
//	short sDate;
//	char cha[2];
//	short sBa[4];
//}*p = (struct Test*)0x100000;
//int main()
//{
//	printf("%p\n", p + 0x1);//p是一个struct Test*类型的指针变量，+1跳过20个字节，20的十六进制是0x00000014
//	//0x00000014 + 0x00100000 = 0x00100014
//	printf("%p\n", (unsigned long)p + 0x1);//这里将struct Test*类型的指针变量p强制转化为unsigned long类型的长整型变量，整型+1就是+1
//	//0x00100000 + 0x00000001 = 0x00100001
//	printf("%p\n", (unsigned int*)p + 0x1);//这里将struct Test*类型的指针变量p强制转化为unsigned int*类型的指针变量，+1跳过4个字节
//	//0x00100000 + 0x00000004 = 0x00100004
//	return 0;
//}

//3
//int main()
//{
//	int a[4] = { 1,2,3,4 };
//	int* ptr1 = (int*)(&a + 1);
//	int* ptr2 = (int*)((int)a + 1);//a是一个数组名，类型为int*,将a强制转化为int类型，然后+1，如果a是数组名时，+1就是2的地址（跳过4字节）
//								//而现在a是一个整型，就相当于在a的地址基础上+1，只跳过一个字节，最后再将其转化为int*类型，占4字节
//	printf("%x,%x", ptr1[-1], *ptr2);
//	//ptr1[-1] -> *(ptr1-1) -> 0x4 -> 0x00000004
//	//0x02000000
//	return 0;
//}

//4
//int main()
//{
//	int a[3][2] = { (0, 1), (2, 3), (4, 5) };//逗号表达式，每一个逗号表达式的结果是最后一个表达式的结果
//	//int a[3][2] = {1,3,5};
//	//1 3
//	//5 0
//	//0 0
//	int* p;
//	p = a[0];//*(a+0) -> *a,第一行的数组
//	printf("%d", p[0]);//*(p+0) -> *p = 1
//	return 0;
//}

//5
//int main()
//{
//	int a[5][5];
//	int(*p)[4];
//	p = a;//p类型是int (*)[4]; a类型是int (*)[5]
//	printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
//	//0xff ff ff fc
//	//-4
//	return 0;
//}

//6
//int main()
//{
//	int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
//	int* ptr1 = (int*)(&aa + 1);
//	int* ptr2 = (int*)(*(aa + 1));
//	printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));//10  5
//	return 0;
//}

//7
//int main()
//{
//	char* a[] = { "work","at","alibaba" };//该数组的类型是char* [3]
//	char** pa = a;//二级指针变量pa存放的是a中第一个字符串的起始位置
//	pa++;
//	printf("%s\n", *pa);//pa++后，此时pa存放的是第二个字符串的起始地址，打印结果是at
//	return 0;
//}

//8
int main()
{
	char* c[] = { "ENTER","NEW","POINT","FIRST" };
	char** cp[] = { c + 3,c + 2,c + 1,c };
	//            "FIRST" "POINT" "NEW" "ENTER" 
	char*** cpp = cp;
	printf("%s\n", **++cpp);//POINT
	printf("%s\n", *-- * ++cpp + 3);//ER
	printf("%s\n", *cpp[-2] + 3);//**(cpp-2)+3 ST
	printf("%s\n", cpp[-1][-1] + 1);//*(*(cpp-1)-1)+1 EW
	return 0;
}
